_INITIAL_BASE_SET = 'b'
_INITIAL_INTRIGUE = 'i'
_INITIAL_PROMO = 'p'
_INITIAL_SEASIDE = 's'
SET_ORDER = [_INITIAL_BASE_SET, _INITIAL_INTRIGUE, _INITIAL_SEASIDE,
             _INITIAL_PROMO,
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9']

UNKNOWN_ICON = '/static/set_unknown.png'


class CardType(object):

  def __init__(self, index, name):
    self.mask = 1 << index
    self.name = name


# Card types
ATTACK = CardType(0, 'Attack')
ACTION = CardType(1, 'Action')
DEFENSE = CardType(2, 'Defense')
DURATION = CardType(3, 'Duration')
REACTION = CardType(4, 'Reaction')
TREASURE = CardType(5, 'Treasure')
VICTORY = CardType(6, 'Victory')

INITIAL_DEFENSE = 'd'
TYPES = {'a': ACTION,
         'b': ATTACK,
         INITIAL_DEFENSE: DEFENSE,
         'e': DURATION,
         'r': REACTION,
         't': TREASURE,
         'v': VICTORY,
        }

PLUS_ACTION = CardType(0, '+ Actions')
PLUS_BUY = CardType(1, '+ Buys')
PLUS_CARD = CardType(2, '+ Cards')
PLUSES = {'a': PLUS_ACTION,
          'b': PLUS_BUY,
          'c': PLUS_CARD,
         }


class CardSet(object):

  def __init__(self, name, icon, check, link, cards):
    self.name = name
    self.icon = icon
    self.check = check
    self.link = link
    self.cards = cards
    self.num_cards = len(cards)

    self.require_cards = False
    self.require_minimum = False
    self.minimum = 3


class Card(object):

  def __init__(self, name, check, cost, types, pluses, icon=None):
    self.name = name
    self.check = check
    self.cost = cost
    self.icon = icon

    if isinstance(types, tuple):
      self.type_mask = reduce(lambda x, y: x | y.mask, types, 0)
    else:
      self.type_mask = types.mask
    if not pluses:
      self.plus_mask = 0
    elif isinstance(pluses, tuple):
      self.plus_mask = reduce(lambda x, y: x | y.mask, pluses, 0)
    else:
      self.plus_mask = pluses.mask

    self.kept = False
    self.prev_set = False

  def Is(self, type):
    return (self.type_mask & type.mask) == type.mask

  def IsPlus(self, plus):
    return (self.plus_mask & plus.mask) == plus.mask

  def IsAttackAttr(self):
    if self.Is(ATTACK):
      return 'is_attack=1'
    return ''

  def __cmp__(self, other):
    return cmp(self.name, other.name)


class CustomCard(Card):
  
  def __init__(self, card_parts, check):
    cost, type_mask, plus_mask, name = card_parts.split('|', 4)
    self.name = name
    self.check = check
    self.cost = int(cost)
    self.icon = None

    self.type_mask = int(type_mask)
    self.plus_mask = int(plus_mask)

    self.kept = False
    self.prev_set = False


def CreateSets(check):
  return {
      _INITIAL_BASE_SET: CardSet(
          'Base set', '/static/set_base_set.png', check,
          '<a href=http://www.amazon.com/gp/product/B001JQY6K4?'
          'ie=UTF8&tag=domicardsele-20&linkCode=as2&camp=1789&'
          'creative=390957&creativeASIN=B001JQY6K4>Base set</a>',
          (Card('Adventurer',      check, 6, ACTION, None),
           Card('Bureaucrat',      check, 4, (ACTION, ATTACK), None),
           Card('Cellar ',         check, 2, ACTION, PLUS_ACTION),
           Card('Chancellor',      check, 3, ACTION, None),
           Card('Chapel ',         check, 2, ACTION, None),
           Card('Council Room',    check, 5, ACTION, (PLUS_BUY, PLUS_CARD)),
           Card('Feast ',          check, 4, ACTION, None),
           Card('Festival',        check, 5, ACTION,
                (PLUS_ACTION, PLUS_BUY)),
           Card('Gardens',         check, 4, VICTORY, None),
           Card('Laboratory',      check, 5, ACTION,
                (PLUS_ACTION, PLUS_CARD)),
           Card('Library',         check, 5, ACTION, None),
           Card('Market',          check, 5, ACTION,
                (PLUS_ACTION, PLUS_BUY, PLUS_CARD)),
           Card('Militia',         check, 4, (ACTION, ATTACK), None),
           Card('Mine',            check, 5, ACTION, None),
           Card('Moat',            check, 2, (ACTION, REACTION, DEFENSE),
                PLUS_CARD),
           Card('Moneylender',     check, 4, ACTION, None),
           Card('Remodel',         check, 4, ACTION, None),
           Card('Smithy',          check, 4, ACTION, PLUS_CARD),
           Card('Spy',             check, 4, (ACTION, ATTACK),
                (PLUS_ACTION, PLUS_CARD)),
           Card('Thief',           check, 4, (ACTION, ATTACK), None),
           Card('Throne Room',     check, 4, ACTION, None),
           Card('Village',         check, 3, ACTION,
                (PLUS_ACTION, PLUS_CARD)),
           Card('Witch',           check, 5, (ACTION, ATTACK), PLUS_CARD),
           Card('Woodcutter',      check, 3, ACTION, PLUS_BUY),
           Card('Workshop',        check, 3, ACTION, None),
          )),
      _INITIAL_INTRIGUE: CardSet(
          'Intrigue', '/static/set_intrigue.png', check,
          '<a href=http://www.amazon.com/gp/product/B002GJMOUC?'
          'ie=UTF8&tag=domicardsele-20&linkCode=as2&camp=1789&'
          'creative=390957&creativeASIN=B002GJMOUC>Intrigue</a>',
          (Card('Baron',           check, 4, ACTION, PLUS_BUY),
           Card('Bridge',          check, 4, ACTION, PLUS_BUY),
           Card('Conspirator',     check, 4, ACTION, None),
           Card('Coppersmith',     check, 4, ACTION, None),
           Card('Courtyard',       check, 2, ACTION, PLUS_CARD),
           Card('Duke',            check, 5, VICTORY, None),
           Card('Great Hall',      check, 3, (ACTION, VICTORY),
                (PLUS_ACTION, PLUS_CARD)),
           Card('Harem',           check, 6, (TREASURE, VICTORY), None),
           Card('Ironworks',       check, 4, ACTION, None),
           Card('Masquerade',      check, 3, ACTION, PLUS_CARD),
           Card('Mining Village',  check, 4, ACTION,
                (PLUS_ACTION, PLUS_CARD)),
           Card('Minion',          check, 5, (ACTION, ATTACK),
                PLUS_ACTION),
           Card('Nobles',          check, 6, (ACTION, VICTORY), None),
           Card('Pawn',            check, 2, ACTION, None),
           Card('Saboteur',        check, 5, (ACTION, ATTACK), None),
           Card('Scout',           check, 4, ACTION, PLUS_ACTION),
           Card('Secret Chamber',  check, 2, (ACTION, REACTION), None),
           Card('Shanty Town',     check, 3, ACTION, PLUS_ACTION),
           Card('Steward',         check, 3, ACTION, None),
           Card('Swindler',        check, 3, (ACTION, ATTACK), None),
           Card('Trading Post',    check, 5, ACTION, None),
           Card('Tribute',         check, 5, ACTION, None),
           Card('Torturer',        check, 5, (ACTION, ATTACK), PLUS_CARD),
           Card('Upgrade',         check, 5, ACTION,
                (PLUS_ACTION, PLUS_CARD)),
           Card('Wishing Well',    check, 3, ACTION,
                (PLUS_ACTION, PLUS_CARD)),
          )),
      _INITIAL_SEASIDE: CardSet(
          'Seaside', '/static/set_seaside.png', check,
          '<a href=http://www.amazon.com/gp/product/B002THXRWK?'
          'ie=UTF8&tag=domicardsele-20&linkCode=as2&camp=1789&'
          'creative=390957&creativeASIN=B002THXRWK>Seaside</a>',
          (Card('Ambassador',      check, 3, ACTION, None),
           Card('Bazaar',          check, 5, ACTION,
                (PLUS_ACTION, PLUS_CARD)),
           Card('Caravan',         check, 4, (ACTION, DURATION),
                (PLUS_ACTION, PLUS_CARD)),
           Card('Cutpurse',        check, 4, (ACTION, ATTACK), None),
           Card('Embargo',         check, 2, ACTION, None),
           Card('Explorer',        check, 5, ACTION, None),
           Card('Fishing Village', check, 3, (ACTION, DURATION),
                PLUS_ACTION),
           Card('Ghost Ship',      check, 5, (ACTION, ATTACK), PLUS_CARD),
           Card('Haven',           check, 2, (ACTION, DURATION),
                (PLUS_ACTION, PLUS_CARD)),
           Card('Island',          check, 4, (ACTION, VICTORY), None),
           Card('Lighthouse',      check, 2, (ACTION, DURATION, DEFENSE),
                PLUS_ACTION),
           Card('Lookout',         check, 3, ACTION, PLUS_ACTION),
           Card('Merchant Ship',   check, 5, (ACTION, DURATION), None),
           Card('Native Village',  check, 2, ACTION, PLUS_ACTION),
           Card('Navigator',       check, 4, ACTION, None),
           Card('Outpost',         check, 5, (ACTION, DURATION), None),
           Card('Pearl Diver',     check, 2, ACTION,
                (PLUS_ACTION, PLUS_CARD)),
           Card('Pirate Ship',     check, 4, (ACTION, ATTACK), None),
           Card('Salvager',        check, 4, ACTION, PLUS_BUY),
           Card('Sea Hag',         check, 4, (ACTION, ATTACK), None),
           Card('Smugglers',       check, 3, ACTION, None),
           Card('Tactician',       check, 5, (ACTION, DURATION), None),
           Card('Treasure Map',    check, 4, ACTION, None),
           Card('Treasury',        check, 5, ACTION,
                (PLUS_ACTION, PLUS_CARD)),
           Card('Warehouse',       check, 3, ACTION,
                (PLUS_ACTION, PLUS_CARD)),
           Card('Wharf',           check, 5, (ACTION, DURATION), None),
          )),
      _INITIAL_PROMO: CardSet(
          'Promo', UNKNOWN_ICON, check, 'Promo',
          (Card('Black Market',    check, 3, ACTION, None,
                icon='/static/set_promo_black_market.png'),
           Card('Envoy',           check, 4, ACTION, None,
                icon='/static/set_promo_envoy.png'),
          )),
     }
